package com.neusoft.neuiotms.dcvm.service.impl;

import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.collections4.map.HashedMap;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.neusoft.bizcore.web.dto.result.ResultDTO;
import com.neusoft.bizcore.web.dto.result.ResultListDTO;
import com.neusoft.bizcore.web.exception.UnitedException;
import com.neusoft.neuiotms.dcvm.bean.AttrBean;
import com.neusoft.neuiotms.dcvm.bean.LocationCiBean;
import com.neusoft.neuiotms.dcvm.bean.StandardCiBean;
import com.neusoft.neuiotms.dcvm.bean.StandardCiBean.SchemaDef;
import com.neusoft.neuiotms.dcvm.service.AssetService;
import com.neusoft.neuiotms.dcvm.service.LocationService;
import com.neusoft.neuiotms.dcvm.task.cmdbSyn.model.enums.LeaseStatus;
import com.neusoft.neuiotms.dcvm.task.cmdbSyn.model.enums.RackTypeStatus;
import com.neusoft.neuiotms.dcvm.utils.CmdbApi;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class AssetServiceImpl implements AssetService {
    @Autowired
    private RestTemplate restTemplate;
    @Value("${cmdb.proxy.target-url}")
    private String cmdbServiceUrl;
    @Autowired
    private LocationService locationService;

    @Override
    public StandardCiBean updateCiAttrs(final String code, final List<AttrBean> attrs) {
        final ParameterizedTypeReference<ResultDTO<StandardCiBean>> typeRef =
                new ParameterizedTypeReference<ResultDTO<StandardCiBean>>() {
                };
        final Map<String, List<AttrBean>> body = new HashMap<>();
        body.put("attrs", attrs);
        final HttpEntity<Map<String, List<AttrBean>>> entity = new HttpEntity<>(body);
        final ResponseEntity<ResultDTO<StandardCiBean>> response = this.restTemplate.exchange(
                this.cmdbServiceUrl + String.format(CmdbApi.updateCiUrl, code), HttpMethod.PUT, entity, typeRef);

        if (null != response) {
            return response.getBody().getData();
        } else {
            throw new UnitedException("调用CMDB无响应");
        }
    }

    @Override
    public StandardCiBean getCiDetail(final String code) {
        final ParameterizedTypeReference<ResultDTO<StandardCiBean>> typeRef =
                new ParameterizedTypeReference<ResultDTO<StandardCiBean>>() {
                };

        final ResponseEntity<ResultDTO<StandardCiBean>> response = this.restTemplate.exchange(
                this.cmdbServiceUrl + String.format(CmdbApi.ciDetailUrl, code), HttpMethod.GET, null, typeRef);
        if (null == response) {
            throw new UnitedException("CMDB未查到任何位置CI项");
        }

        final ResultDTO<StandardCiBean> result = response.getBody();

        final StandardCiBean data = result.getData();

        return data;
    }

    @Override
    public List<StandardCiBean> getLocDeepCi(final String locCode, final int deep) {

        final List<LocationCiBean> locations = this.locationService.getDeepLocationsByOne(locCode, deep);
        final ParameterizedTypeReference<ResultListDTO<StandardCiBean>> typeRef =
                new ParameterizedTypeReference<ResultListDTO<StandardCiBean>>() {
                };

        locations.stream().map(it -> "location=" + it.getCode()).collect(Collectors.toList());

        final String query = StringUtils
                .join(locations.stream().map(it -> "location=" + it.getCode()).collect(Collectors.toList()), "&");
        final ResponseEntity<ResultListDTO<StandardCiBean>> response = this.restTemplate
                .exchange(this.cmdbServiceUrl + CmdbApi.filterCiUrl + "?" + query, HttpMethod.GET, null, typeRef);

        if (null == response) {
            throw new UnitedException("CMDB未查到任何位置CI项");
        }

        final ResultListDTO<StandardCiBean> result = response.getBody();

        return result.getData();
    }

    @Override
    public List<StandardCiBean> getAllCiByCatetories(final String category) {
        final String uri = this.cmdbServiceUrl + String.format(CmdbApi.allCiByCatetoryUrl, category);
        return this.getAllCi(uri);
    }

    @Override
    public List<StandardCiBean> getAllCiBySchema(final String schema) {
        final String uri = this.cmdbServiceUrl + String.format(CmdbApi.allCiBySchemaUrl, schema);
        return this.getAllCi(uri);
    }

    @Override
    public List<StandardCiBean> getAllCiByForeignInst(final String foreignKey, final String foreignValue) {
        final String uri = this.cmdbServiceUrl + String.format(CmdbApi.allCiByForeignInstUrl, foreignKey, foreignValue);
        return this.getAllCi(uri);
    }

    @Override
    public List<SchemaDef> getAllSchemasByCatetories(final String category) {
        final ParameterizedTypeReference<ResultListDTO<SchemaDef>> typeRef =
                new ParameterizedTypeReference<ResultListDTO<SchemaDef>>() {
                };

        final ResponseEntity<ResultListDTO<SchemaDef>> response = this.restTemplate.exchange(
                this.cmdbServiceUrl + String.format(CmdbApi.allSchemasByCatetoryUrl, category), HttpMethod.GET, null,
                typeRef);
        return response.getBody().getData();
    }

    @Override
    public List<StandardCiBean> getAllCiByCatetoriesAndForeign(final String category, final String foreignKey,
            final String foreignValue) {
        final String uri = this.cmdbServiceUrl
                + String.format(CmdbApi.allCiByCatetoryAndForeignUrl, category, foreignKey, foreignValue);
        return this.getAllCi(uri);
    }

    private List<StandardCiBean> getAllCi(final String uri) {
        final ParameterizedTypeReference<ResultListDTO<StandardCiBean>> typeRef =
                new ParameterizedTypeReference<ResultListDTO<StandardCiBean>>() {
                };

        final ResponseEntity<ResultListDTO<StandardCiBean>> response = this.restTemplate.exchange(uri, HttpMethod.GET,
                null, typeRef);
        return response.getBody().getData();
    }

    @Override
    public JSONObject createInstances(final String instances) {
        final URI uri =
                UriComponentsBuilder.fromUriString(this.cmdbServiceUrl + String.format(CmdbApi.ciCreateUrl)).build()
                        .toUri();
        final RequestEntity<String> requestEntity = RequestEntity.post(uri)
                .accept(org.springframework.http.MediaType.APPLICATION_JSON).header("Content-Type", "application/json")
                .body(instances);

        final ResponseEntity<String> response = this.restTemplate.exchange(requestEntity, String.class);

        if (null != response) {

            return JSON.parseObject(response.getBody());
        } else {
            throw new UnitedException("调用CMDB无响应");
        }
    }

    @Override
    public JSONObject updateInstancesAttrs(final String code, final List<AttrBean> attrs) {
        final ParameterizedTypeReference<JSONObject> typeRef = new ParameterizedTypeReference<JSONObject>() {
        };
        final Map<String, List<AttrBean>> body = new HashMap<>();
        body.put("attrs", attrs);
        final HttpEntity<Map<String, List<AttrBean>>> entity = new HttpEntity<>(body);
        final ResponseEntity<JSONObject> response = this.restTemplate.exchange(
                this.cmdbServiceUrl + String.format(CmdbApi.updateCiUrl, code), HttpMethod.PUT, entity, typeRef);

        if (null != response) {
            return response.getBody();
        } else {
            throw new UnitedException("调用CMDB无响应");
        }
    }

    @Override
    public JSONObject globalSearch(final String name) {
        final String uri = this.cmdbServiceUrl + CmdbApi.globalsearchUrl;
        final ParameterizedTypeReference<JSONObject> typeRef = new ParameterizedTypeReference<JSONObject>() {
        };
        final String param = "?withattrs=1&s_kw=" + name;
        final ResponseEntity<JSONObject> response = this.restTemplate.exchange(uri + param, HttpMethod.GET, null,
                typeRef);
        return response.getBody();
    }

    @Override
    @Cacheable(cacheNames = "AssetRackLeaseStatus", key = "#PosCode")
    public List<JSONObject> rackLeaseStatus(final String PosCode) {
        List<JSONObject> result = null;
        try {
            final Map<String, String> leaseStatusMap = this.getLeaseStatusMap();
            final List<StandardCiBean> matchJG = this.getMatchJG(PosCode);
            result = matchJG.stream().map(x -> {
                final List<AttrBean> filterAttr =
                        x.getAttrs().stream().filter(a -> a.getKey().equals("leaseStatus") && (null != a.getValue()))
                                .collect(Collectors.toList());
                if (filterAttr.size() > 0) {
                    final JSONObject item = new JSONObject();
                    item.put("code", x.getCode());
                    final String value = leaseStatusMap.get(filterAttr.get(0).getValue().toString());
                    if (StringUtils.isBlank(value)) {
                        return null;
                    }
                    item.put("value", value);
                    return item;
                }
                return null;
            }).filter(y -> null != y).collect(Collectors.toList());
        } catch (final Exception e) {
            AssetServiceImpl.log.error("", e);
        }
        return result;
    }

    @Override
    @Cacheable(cacheNames = "AssetRackType", key = "#PosCode")
    public List<JSONObject> rackType(final String PosCode) {
        List<JSONObject> result = null;
        try {
            final Map<String, String> rackTypeStatusMap = this.getRackTypeStatusMap();
            final List<StandardCiBean> matchJG = this.getMatchJG(PosCode);
            result = matchJG.stream().map(x -> {
                final List<AttrBean> filterAttr =
                        x.getAttrs().stream().filter(a -> a.getKey().equals("rackType") && (null != a.getValue()))
                                .collect(Collectors.toList());
                if (filterAttr.size() > 0) {
                    final JSONObject item = new JSONObject();
                    item.put("code", x.getCode());
                    final String value = rackTypeStatusMap.get(filterAttr.get(0).getValue().toString());
                    item.put("value", value);
                    if (StringUtils.isBlank(value)) {
                        return null;
                    }
                    return item;
                }
                return null;
            }).filter(y -> null != y).collect(Collectors.toList());
        } catch (final Exception e) {
            AssetServiceImpl.log.error("", e);
        }
        return result;
    }

    @Override
    public List<Map<String, Object>> rackCapacity(final String PosCode) {
        // 根据location查询所有的子位置
        final List<LocationCiBean> locations = this.locationService.getDeepLocationsByOne(PosCode, Integer.MAX_VALUE);
        final String foreignValue =
                Joiner.on(",").join(locations.stream().map(it -> it.getCode()).collect(Collectors.toList()));
        // 查询指定位置下的所有机柜
        final List<StandardCiBean> allCabinets =
                this.getAllCiByCatetoriesAndForeign("CT_RACK", "location", foreignValue);

        final List<Map<String, Object>> infos = new ArrayList<>();
        for (final StandardCiBean cabinet : allCabinets) {

            final AttrBean uHeightAttr =
                    cabinet.getAttrs().stream().filter(it -> "uHeight".equalsIgnoreCase(it.getKey()))
                            .findFirst().orElse(null);

            final AttrBean uInfoAttr =
                    cabinet.getAttrs().stream().filter(it -> "uInfo".equalsIgnoreCase(it.getKey()))
                            .findFirst().orElse(null);

            final AttrBean uCapacityAttr =
                    cabinet.getAttrs().stream().filter(it -> "uCapacity".equalsIgnoreCase(it.getKey()))
                            .findFirst().orElse(null);

            final AttrBean usedUAttr =
                    cabinet.getAttrs().stream().filter(it -> "usedU".equalsIgnoreCase(it.getKey()))
                            .findFirst().orElse(null);

            final AttrBean ratedPowerAttr =
                    cabinet.getAttrs().stream().filter(it -> "ratedPower".equalsIgnoreCase(it.getKey()))
                            .findFirst().orElse(null);

            final AttrBean eCapacityAttr =
                    cabinet.getAttrs().stream().filter(it -> "eCapacity".equalsIgnoreCase(it.getKey()))
                            .findFirst().orElse(null);

            final AttrBean wCapacityAttr =
                    cabinet.getAttrs().stream().filter(it -> "wCapacity".equalsIgnoreCase(it.getKey()))
                            .findFirst().orElse(null);

            final AttrBean realWeightAttr =
                    cabinet.getAttrs().stream().filter(it -> "realWeight".equalsIgnoreCase(it.getKey()))
                            .findFirst().orElse(null);

            final Map<String, Object> space = new HashedMap<>();
            space.put("unit", "U");
            space.put("total", Double.valueOf(String.valueOf(uHeightAttr.getValue())));
            space.put("uInfo", uInfoAttr.getValue());
            space.put("percent", Double.valueOf(String.valueOf(uCapacityAttr.getValue())));
            space.put("used", Double.valueOf(String.valueOf(usedUAttr.getValue())));

            final Map<String, Object> electric = new HashedMap<>();
            electric.put("unit", "W");
            electric.put("total", ratedPowerAttr.getValue());
            electric.put("percent", Double.valueOf(String.valueOf(eCapacityAttr.getValue())));
            electric.put("used", Double.valueOf(String.valueOf(ratedPowerAttr.getValue()))
                    * Double.valueOf(String.valueOf(eCapacityAttr.getValue())));

            final Map<String, Object> weight = new HashedMap<>();
            weight.put("unit", "KG");
            weight.put("total", 1000.0);
            weight.put("percent", Double.valueOf(String.valueOf(wCapacityAttr.getValue())));
            weight.put("used", Double.valueOf(String.valueOf(realWeightAttr.getValue())));

            final Map<String, Object> capaInfo = new HashedMap<>();
            capaInfo.put("electric", electric);
            capaInfo.put("weight", weight);
            capaInfo.put("space", space);

            final Map<String, Object> info = new HashedMap<>();
            info.put("cabinetCode", cabinet.getCode());
            info.put("capaInfo", capaInfo);

            infos.add(info);
        }
        return infos;
    }

    /**
     * 层编码
     *
     * @param PosCode
     * @return
     */
    private List<StandardCiBean> getMatchJG(final String PosCode) {
        //数据中心-机楼-层-房间-列-机柜
        //子类型属性sub_location_type：PARK-BUILD-FLOOR-ROOM-LIE
        final List<StandardCiBean> allMatchJG = new ArrayList<>();
        List<LocationCiBean> deepLocationsByOne = this.locationService.getDeepLocationsByOne(PosCode, 2);

        if (AssetServiceImpl.log.isDebugEnabled()) {
            AssetServiceImpl.log.debug("{}: {}", PosCode, JSON.toJSONString(deepLocationsByOne));
        }

        deepLocationsByOne =
                deepLocationsByOne.stream().filter(x -> x.getAttrs().stream().anyMatch(s -> "LIE".equals(s.getValue())))
                        .collect(Collectors.toList());
        if (AssetServiceImpl.log.isDebugEnabled()) {
            AssetServiceImpl.log.debug("{}: {}", PosCode, JSON.toJSONString(deepLocationsByOne));
        }
        deepLocationsByOne.forEach(item -> {
            final List<StandardCiBean> itemCiByForeignInst = this.getAllCiByCatetoriesAndForeign("CT_RACK", "location",
                    item.getCode());
            allMatchJG.addAll(itemCiByForeignInst);
        });

        if (AssetServiceImpl.log.isDebugEnabled()) {
            AssetServiceImpl.log.debug("{}: {}", PosCode, JSON.toJSONString(allMatchJG));
        }

        return allMatchJG;

    }

    private Map<String, String> getLeaseStatusMap() {
        final Map<String, String> map = new HashMap<>();
        final LeaseStatus[] values = LeaseStatus.values();
        for (final LeaseStatus status : values) {
            map.put(status.getValue(), status.getDesc());
        }
        return map;
    }

    private Map<String, String> getRackTypeStatusMap() {
        final Map<String, String> map = new HashMap<>();
        final RackTypeStatus[] values = RackTypeStatus.values();
        for (final RackTypeStatus status : values) {
            map.put(status.getValue(), status.getDesc());
        }
        return map;
    }

    @Override
    public Map<String, Object> getAssetLocation(String assetCode) {
        final StandardCiBean ci = this.getCiDetail(assetCode);
        final Map<String, Object> result = new HashMap<>();
        result.put("_id", ci.get_id());
        result.put("name", ci.getName());
        result.put("code", ci.getCode());
        final List<AttrBean> attrs = Lists.newArrayList();
        result.put("attrs", attrs);

        ci.getAttrs().forEach(attr -> {
            if ("location".equals(attr.getKey())) {
                attrs.add(attr);
            } else if ("cabinet".equals(attr.getKey()) || "pdg_code".equals(attr.getKey())) {
                attrs.add(attr);
                final StandardCiBean cabinetCi = this.getCiDetail((String) attr.getValue());
                final AttrBean cabinetAttr = cabinetCi.getAttrs().stream().filter(it -> "location".equals(it.getKey()))
                        .findFirst().orElse(null);
                if (null != cabinetAttr) {
                    attrs.add(cabinetAttr);
                }
            }
        });
        return result;
    }

    @Override
    public JSONObject filter(String attrKey, String attrValue) {
        final String uri = this.cmdbServiceUrl + CmdbApi.filterCiUrl;
        final ParameterizedTypeReference<JSONObject> typeRef = new ParameterizedTypeReference<JSONObject>() {
        };
        final String param = "?" + attrKey + "=" + attrValue;
        final ResponseEntity<JSONObject> response = this.restTemplate.exchange(uri + param, HttpMethod.GET, null,
                typeRef);
        return response.getBody();
    }

    @Override
    public JSONObject deleteInstance(String code) {
        final ParameterizedTypeReference<JSONObject> typeRef = new ParameterizedTypeReference<JSONObject>() {
        };
        final ResponseEntity<JSONObject> response = this.restTemplate.exchange(
                this.cmdbServiceUrl + String.format(CmdbApi.ciDetailUrl, code), HttpMethod.DELETE, null, typeRef);

        if (null != response) {
            return response.getBody();
        } else {
            throw new UnitedException("调用CMDB无响应");
        }
    }

}
